#include <GeomAPI_ProjectPointOnCurve.hxx>
#include <Geom_Curve.hxx>
#include <Geom2d_Curve.hxx>

#include "Qt3DPicker.h"
#include "QtOSGWidget.h"
#include "OsgUtil.h"
#include "RotateHandler.h"
#include "CmdManager.h"
#include "Scene.h"
#include "SendPointHandlerBase.h"
#include "SelectHandler3D.h"
#include "OSGNodeMask.h"
#include "Operation/OpReplace.h"
#include "Plane.h"
#include "LineStrip3D.h"
#include "Arc3D.h"
#include "Spline3D.h"
#include "EntFromEdge.h"
#include "Pline2D.h"

RotateHandler::RotateHandler()
{
}

RotateHandler::~RotateHandler()
{
}

bool getStartDir(const  std::shared_ptr<Ent>& ent, osg::Vec3d& startPt, osg::Vec3d& startDir);

bool getEndDir(const  std::shared_ptr<Ent>& ent, osg::Vec3d& endPt, osg::Vec3d& endDir)
{
	auto ent2d = dynamic_cast<Ent2DCurve*>(ent.get());
	if (ent2d)
	{
		auto curve2ds = ent2d->getCurve2d();
		if (curve2ds.empty())
			return false;
		auto curve2d = curve2ds[0];
		gp_Pnt2d endP;
		gp_Vec2d endV;
		curve2d->D1(curve2d->LastParameter(), endP, endV);
		UCS* ucs = dynamic_cast<UCS*>(ent->getParent());
		if (!ucs)
			return false;
		auto matrix = ucs->getMatrix();
		endPt = { double(endP.X()), double(endP.Y()),0 };
		endDir = { double(endV.X()), double(endV.Y()), 0 };
		endPt = endPt * matrix;
		// 方向变换，不是点变换，所以去掉平移分量。注意transform3x3参数的顺序
		endDir = osg::Matrixd::transform3x3(endDir, matrix);
	}
	else
	{
		auto ent3d = dynamic_cast<Ent3DCurve*>(ent.get());
		auto curve3ds = ent3d->getCurve3d();
		if (curve3ds.empty())
			return false;
		auto curve3d = curve3ds[0];
		gp_Pnt endP;
		gp_Vec endV;
		curve3d->D1(curve3d->LastParameter(), endP, endV);
		endPt = { double(endP.X()), double(endP.Y()), double(endP.Z()) };
		endDir = { double(endV.X()), double(endV.Y()), double(endV.Z()) };
	}
	return true;
}

std::shared_ptr<Ent3DCurve> rotateEnt3D(const std::shared_ptr<Ent3D> & ent3D,
	INode* parent, const osg::Matrixd& matrix)
{
	if (ent3D->metaObject() == &LineStrip3D::staticMetaObject)
	{
		auto line3d = std::static_pointer_cast<LineStrip3D>(ent3D);
		auto points = line3d->getPoints();
		for (auto& pt : points)
		{
			pt = pt * matrix;
		}
		auto line = std::make_shared<LineStrip3D>(parent);
		line->setPoints(points);
		return line;
	}
	else if (ent3D->metaObject() == &Arc3D::staticMetaObject)
	{
		auto arc3d = std::static_pointer_cast<Arc3D>(ent3D);
		osg::Vec3d center = arc3d->center() * arc3d->getMatrix();
		osg::Vec3d xAxis = osg::Matrix::transform3x3(arc3d->xAxis(), matrix);
		osg::Vec3d yAxis = osg::Matrix::transform3x3(arc3d->yAxis(), matrix);
		auto arc = std::make_shared<Arc3D>(parent);
		arc->setAxis(xAxis, yAxis);
		arc->setOrigin(center * matrix);
		arc->set(osg::Vec3d(), arc3d->radius(), arc3d->startAngle(), arc3d->endAngle());
		return arc;
	}
	else if (ent3D->metaObject() == &Spline3D::staticMetaObject)
	{
		auto spline3d = std::static_pointer_cast<Spline3D>(ent3D);
		auto controlPts = spline3d->getCtrlPoints();
		for (auto& pt : controlPts)
		{
			pt = pt * matrix;
		}
		auto spline = std::make_shared<Spline3D>(parent);
		spline->setDegree(spline3d->getDegree());
		spline->setFlag(spline3d->getFlag());
		spline->setCtrlPoints(controlPts);
		spline->setWeights(spline3d->getWeights());
		spline->setKnots(spline3d->getKnots());
		return spline;
	}
	return std::shared_ptr<Ent3DCurve>();
}

void RotateHandler::input(const QVariant& data, const QString& rawInput, CommandSource cs)
{
	auto scene = getScene();
	auto sph = getSendPointHandler();
	auto selector = qobject_cast<SelectHandler3D*>(getSelectHandler());
	if (data.type() == QVariant::Bool)
	{
		bool esc = !data.toBool();
		if (esc)
		{
			selector->clearSelection();
			clearInput();
			emit finished();
			return;
		}
	}

	if (status() == 0)
	{
		if (selector)
		{
			selector->clearSelection();
			selector->beginSelect(D2LineMask | D2CurveMask | D3LineMask | D3CurveMask);
			prompt(1, tr("选择要旋转的对象: "));
		}
		else
		{
			emit finished();
		}
		return;
	}
	if (status() == 1)
	{
		if (selector && selector->done())
		{
			std::set<std::shared_ptr<INode>> selections = selector->selectionSet();
			if (selections.empty())
			{
				emit finished();
				return;
			}
			for (auto selection : selections)
			{
				auto ent = std::static_pointer_cast<Ent>(selection);
				if (ent->metaObject() == &Pline2D::staticMetaObject)
				{
					auto pline = std::static_pointer_cast<Pline2D>(ent);
					std::vector<std::shared_ptr<Ent>> explodeEnts;
					bool success = pline->explode(explodeEnts);
					if (success)
					{
						for (auto explodeEnt : explodeEnts)
						{
							auto ent2d = std::static_pointer_cast<Ent2D>(explodeEnt);
							m_origins.push_back(ent2d);
						}
					}
				}
				else
				{
					m_origins.push_back(ent);
				}
			}
			selector->clearSelection();
			selector->beginSelect(D2LineMask | D2CurveMask | D3LineMask | D3CurveMask, 1);
			prompt(2, tr("选择旋转轴: "));
		}
	}
	if (status() == 2)
	{
		if (selector && selector->done())
		{
			auto selections = selector->selectionSet();
			if (selections.size() > 0)
			{
				auto selection = *selections.begin();
				std::shared_ptr<Ent> ent = std::dynamic_pointer_cast<Ent>(selection);
				bool success = getNearEndPtAndDir(ent);			
				if (!success)
				{
					remark(tr("获取旋转轴方向失败"));
					emit finished();
					return;
				}
				if (sph)
				{
					sph->setPointMask(CS_LBTNDOWN | CS_MOUSEMOVE);
				}
				enableMouseHandler(MouseHandlerID::PointSender);
				prompt(3, tr("指定旋转的起点或角度:"));
			}
			else
			{
				selector->clearSelection();
				selector->beginSelect(D2LineMask | D2CurveMask | D3LineMask | D3CurveMask, 1);
				remark(tr("重新选择旋转轴"));
			}
		}
		return;
	}

	if(status() == 3)
	{
		if (data.type() == QVariant::Double)
		{
			double angle = data.value<double>();
			const double PI = 3.14159265358979323846;
			angle = angle * PI / 180;
			std::vector<std::shared_ptr<Ent>> result;
			rotate(result, scene, angle);
			auto undoStack = scene->undoStack();
			undoStack->push(new OpReplace(m_origins, result));
			selector->clearSelection();
			emit finished();
			return;
		}
		else if (data.type() == QVariant::Vector3D)
		{
			if (cs == CS_MOUSEMOVE)
				return;
			auto pt = data.value<QVector3D>();
			m_start = { pt.x(), pt.y(), pt.z() };
			prompt(4, tr("指定终点:"));
		}
		return;
	}

	if (status() == 4)
	{
		double angle;		
		if(data.type() == QVariant::Vector3D)
		{
			if (cs == CS_MOUSEMOVE)
				return;
			auto pt = data.value<QVector3D>();
			m_end = { pt.x(), pt.y(), pt.z() };
			osg::Vec3d startV = m_start - m_pt;
			osg::Vec3d endV = m_end - m_pt;
			if (startV.length() == 0 || endV.length() == 0)
			{
				remark(tr("起始点和端点重合"));
				emit finished();
				return;
			}
			gp_Dir startDir = { startV.x(), startV.y(), startV.z() };
			gp_Dir endDir = { endV.x(), endV.y(), endV.z() };
			gp_Dir refDir = { m_ptDir.x(), m_ptDir.y(), m_ptDir.z() };
			angle = startDir.AngleWithRef(endDir,refDir);
		}
		std::vector<std::shared_ptr<Ent>> result;
		rotate(result, scene, angle);
		auto undoStack = scene->undoStack();
		undoStack->push(new OpReplace(m_origins, result));
		selector->clearSelection();
		emit finished();
		return;
	}
}

void RotateHandler::rotate(std::vector<std::shared_ptr<Ent>>& result, Scene* scene, double angle)
{
	osg::Matrixd t1, t2, rot, matrix;
	t1.makeTranslate(-m_pt);
	t2.makeTranslate(m_pt);
	rot.makeRotate(angle, m_ptDir);
	matrix = t1 * rot * t2;
	for (auto ent : m_origins)
	{
		std::shared_ptr<Ent3D> ent3d = std::dynamic_pointer_cast<Ent3D>(ent);
		if (ent3d)
		{
			std::shared_ptr<Ent3DCurve> ent3d = std::static_pointer_cast<Ent3DCurve>(ent);
			auto ent3dCurve = rotateEnt3D(ent3d, scene, matrix);
			ent3dCurve->setColor(getCurrentColor());
			result.push_back(ent3dCurve);
		}
		else
		{
			std::shared_ptr<Ent2D> ent2d = std::static_pointer_cast<Ent2D>(ent);
			auto ent3d = makeEnt3DFromEnt2D(ent2d, scene, matrix);
			std::shared_ptr<Ent3DCurve> ent3dCurve = std::static_pointer_cast<Ent3DCurve>(ent3d);
			ent3dCurve->setColor(getCurrentColor());
			result.push_back(ent3dCurve);
		}
	}
}

bool RotateHandler::getNearEndPtAndDir(std::shared_ptr<Ent>& ent)
{
	std::shared_ptr<Ent3D> ent3d = std::dynamic_pointer_cast<Ent3D>(ent);
	osg::Vec3d start, end;
	if (ent3d)
	{
		std::shared_ptr<Ent3DCurve> ent3d = std::static_pointer_cast<Ent3DCurve>(ent);
		auto curves = ent3d->getCurve3d();
		gp_Pnt startPnt;
		curves[0]->D0(curves[0]->FirstParameter(), startPnt);
		gp_Pnt endPnt;
		int lastCurveIndex = curves.size() - 1;
		curves[lastCurveIndex]->D0(curves[lastCurveIndex]->LastParameter(), endPnt);
		start = osg::Vec3d(startPnt.X(), startPnt.Y(), startPnt.Z());
		end = osg::Vec3d(endPnt.X(), endPnt.Y(), endPnt.Z());
	}
	else
	{
		std::shared_ptr<Ent2DCurve> ent2d = std::static_pointer_cast<Ent2DCurve>(ent);
		auto curves = ent2d->getCurve2d();
		gp_Pnt2d startPnt;
		curves[0]->D0(curves[0]->FirstParameter(), startPnt);
		gp_Pnt2d endPnt;
		int lastCurveIndex = curves.size() - 1;
		curves[lastCurveIndex]->D0(curves[lastCurveIndex]->LastParameter(), endPnt);
		start = osg::Vec3d(startPnt.X(), startPnt.Y(), 0);
		end = osg::Vec3d(endPnt.X(), endPnt.Y(), 0);
		auto parent = ent2d->getParent();
		UCS* ucs = static_cast<UCS*>(parent);
		auto matrix = ucs->getMatrix();
		start = start * matrix;
		end = end * matrix;
	}
	auto widget3D = getWidget();
	osg::Vec2d cursor = widget3D->getCursorPoint();
	osg::Camera* camera = widget3D->getCamera();
	if (!camera)
	{
		emit finished();
		return false;
	}
	osg::Matrix VPW, invVPW;
	bool success = getCameraViewProjectionWindowMatrix(camera, VPW, invVPW);
	if (!success)
	{
		emit finished();
		return false;
	}
	osg::Vec3d cursorPt = { cursor.x(), cursor.y(), 0 };
	//转换到屏幕坐标系
	start = start * VPW;
	end = end * VPW;
	osg::Vec3d startV = cursorPt - start;
	osg::Vec3d endV = cursorPt - end;
	if (startV.length() < endV.length())
	{
		success = getStartDir(ent, m_pt, m_ptDir);
	}
	else
	{
		success = getEndDir(ent, m_pt, m_ptDir);
	}
	return true;
}

